#ifndef FILEIOWINDOWS_HPP
#define FILEIOWINDOWS_HPP

#include <time.h>
#include <string>
#include <vector>
#include <windows.h>
//#include <strsafe.h>

class FileIOWindows
{
public:

	static void SpiltFullPath(std::wstring &folder, std::wstring &filename, std::wstring path)
	{
		std::size_t found = path.find_last_of(L"/\\");
		folder = path.substr(0, found + 1);
		filename = path.substr(found + 1);
	}

	static void SpiltExtention(std::wstring &name, std::wstring &extention, std::wstring filename)
	{
		std::size_t found = filename.find_last_of(L".");
		name = filename.substr(0, found);
		extention = filename.substr(found);
	}

	static std::string TimeString(unsigned int bSeg = 0)
	{
		time_t rawtime;
		time(&rawtime);
		struct tm *timeinfo = localtime(&rawtime);
		const size_t l = 20;
		char buffer[l];
		switch (bSeg)
		{
		case 1:
			strftime(buffer, l, "%Y-%m-%d %H:%M:%S", timeinfo);
			break;
		case 2:
			strftime(buffer, l, "%Y%m%d_%H%M%S", timeinfo);
			break;
		default:
			strftime(buffer, l, "%Y%m%d%H%M%S", timeinfo);
			break;
		}
		std::string re(buffer);
		return re;
	};

	static std::wstring TimeStringW(unsigned int bSeg = 0)
	{
		std::string ts = TimeString(bSeg);
		return std::wstring(ts.begin(), ts.end());
	}

	static void FixSlash(std::wstring &folder)
	{
		if (folder.back() == L'/')
			folder.pop_back();
		if (folder.back() != L'\\')
			folder.push_back(L'\\');
	}

	static void SetFolder(const std::wstring &fop)
	{
		DWORD dwAttr = GetFileAttributesW(fop.c_str());
		if (dwAttr == 0xFFFFFFFF) //INVALID_FILE_ATTRIBUTES
			CreateDirectoryW(fop.c_str(), NULL);
	};

	static void GetFolderContentList(std::vector<std::wstring> &fileList, std::vector<std::wstring> &folderList, std::wstring folder, bool removeParentSelf = true)
	{
		FixSlash(folder);

		WIN32_FIND_DATAW ffd;
		HANDLE hFind = INVALID_HANDLE_VALUE;

#if 0
		wchar_t szDir[MAX_PATH];
		size_t length_of_arg;


		// If the directory is not specified as a command-line argument,
		// print usage.


		// Check that the input path plus 3 is not longer than MAX_PATH.
		// Three characters are for the "\*" plus NULL appended below.

		StringCchLengthW(folder.c_str(), MAX_PATH, &length_of_arg);

		if (length_of_arg > (MAX_PATH - 3))
		{
			return;
		}

		// Prepare string for use with FindFile functions.  First, copy the
		// string to a buffer, then append '\*' to the directory name.

		StringCchCopyW(szDir, MAX_PATH, folder.c_str());
		StringCchCatW(szDir, MAX_PATH, L"*");

		// Find the first file in the directory.

		hFind = FindFirstFileW(szDir, &ffd);
#else
		std::wstring folderE = folder + L"*";
		hFind = FindFirstFileW(folderE.c_str(), &ffd);
#endif

		if (INVALID_HANDLE_VALUE == hFind)
		{
			return;
		}

		// List all the files in the directory with some info about them.

		do
		{
			if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				if (removeParentSelf)
				{
					std::wstring tmpp(ffd.cFileName);
					if (tmpp.compare(L".") == 0)
						continue;
					if (tmpp.compare(L"..") == 0)
						continue;
				}
				folderList.push_back(ffd.cFileName);
			}
			else
			{
				fileList.push_back(ffd.cFileName);
			}
		} while (FindNextFileW(hFind, &ffd) != 0);
		FindClose(hFind);
	};

	static void GetFolderContentListRFullPath(std::vector<std::wstring> &fileList, std::vector<std::wstring> &folderList, std::wstring folder)
	{
		FixSlash(folder);

		WIN32_FIND_DATAW ffd;
		HANDLE hFind = INVALID_HANDLE_VALUE;

#if 0
		wchar_t szDir[MAX_PATH];
		size_t length_of_arg;


		// If the directory is not specified as a command-line argument,
		// print usage.


		// Check that the input path plus 3 is not longer than MAX_PATH.
		// Three characters are for the "\*" plus NULL appended below.

		StringCchLengthW(folder.c_str(), MAX_PATH, &length_of_arg);

		if (length_of_arg > (MAX_PATH - 3))
		{
			return;
		}

		// Prepare string for use with FindFile functions.  First, copy the
		// string to a buffer, then append '\*' to the directory name.

		StringCchCopyW(szDir, MAX_PATH, folder.c_str());
		StringCchCatW(szDir, MAX_PATH, L"*");

		// Find the first file in the directory.

		hFind = FindFirstFileW(szDir, &ffd);
#else
		std::wstring folderE = folder + L"*";
		hFind = FindFirstFileW(folderE.c_str(), &ffd);
#endif
		if (INVALID_HANDLE_VALUE == hFind)
		{
			return;
		}

		// List all the files in the directory with some info about them.

		do
		{
			if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
			{
				std::wstring tmpp(ffd.cFileName);

				if (tmpp.compare(L".") == 0)
					continue;

				if (tmpp.compare(L"..") == 0)
					continue;

				GetFolderContentListRFullPath(fileList, folderList, folder + tmpp + L"\\");
				folderList.push_back(folder + tmpp);
			}
			else
			{
				fileList.push_back(folder + ffd.cFileName);
			}
		} while (FindNextFileW(hFind, &ffd) != 0);
		FindClose(hFind);
	};

	static void GetFolderContentListR(std::vector<std::wstring> &fileList, std::vector<std::wstring> &folderList, std::wstring folder)
	{
		FixSlash(folder);

		GetFolderContentListRFullPath(fileList, folderList, folder);

		for (size_t i = 0; i < fileList.size(); i++)
			fileList[i].erase(fileList[i].begin(), fileList[i].begin() + folder.size());

		for (size_t i = 0; i < folderList.size(); i++)
			folderList[i].erase(folderList[i].begin(), folderList[i].begin() + folder.size());
	}

	static void DeleteFolderR(std::wstring fop)
	{
		FixSlash(fop);
		DWORD dwAttr = GetFileAttributesW(fop.c_str());
		if (dwAttr != 0xFFFFFFFF)
		{
			std::vector<std::wstring> fnl, fol;
			GetFolderContentList(fnl, fol, fop);

			for (size_t j = 0; j < fnl.size(); j++)
			{
				std::wstring fp = fop + fnl[j];
				::DeleteFileW(fp.c_str());
			}
			for (size_t j = 0; j < fol.size(); j++)
			{
				DeleteFolderR(fop + fol[j]);
			}
			::RemoveDirectoryW(fop.c_str());
		}
	};


	static void DeleteEmptyFolderR(std::wstring fop, std::vector<std::wstring> &emptyFolderList)
	{
		FixSlash(fop);
		DWORD dwAttr = GetFileAttributesW(fop.c_str());
		if (dwAttr != 0xFFFFFFFF)
		{
			std::vector<std::wstring> fnl, fol;
			GetFolderContentList(fnl, fol, fop);

			for (size_t j = 0; j < fol.size(); j++)
			{
				DeleteEmptyFolderR(fop + fol[j], emptyFolderList);
			}

			fnl.clear();
			fol.clear();
			GetFolderContentList(fnl, fol, fop);

			if (fnl.empty() && fol.empty())
			{
				::RemoveDirectoryW(fop.c_str());
				emptyFolderList.push_back(fop);
			}
		}
	};


	static void MergeEmptyFolderR(std::wstring fop, std::vector<std::wstring> &mergeFolderList, bool keepOuter=true)
	{
		FixSlash(fop);
		DWORD dwAttr = GetFileAttributesW(fop.c_str());
		if (dwAttr != 0xFFFFFFFF)
		{
			std::vector<std::wstring> fnl, fol;
			GetFolderContentList(fnl, fol, fop);

			for (size_t j = 0; j < fol.size(); j++)
			{
				MergeEmptyFolderR(fop + fol[j], mergeFolderList, keepOuter);
			}

			fnl.clear();
			fol.clear();
			GetFolderContentList(fnl, fol, fop);

			if (fnl.empty() && fol.size() == 1)
			{
				std::wstring fopin = fop + fol[0];
				fop.pop_back();
				std::wstring fopParent;
				std::wstring nameOut;
				SpiltFullPath(fopParent, nameOut, fop);
				std::wstring temp = fopParent + TimeStringW();
				if (::MoveFileW(fopin.c_str(), temp.c_str()) != FALSE)
				{
					if (::RemoveDirectoryW(fop.c_str()) != FALSE)
					{
						std::wstring fopOut;
						if (keepOuter)
						{
							fopOut = fop;
							mergeFolderList.push_back(fopParent + fol[0]);
						}
						else
						{
							fopOut = fopParent + fol[0];
							mergeFolderList.push_back(fop);
						}

						::MoveFileW(temp.c_str(), fopOut.c_str());						
					}
					else
					{
						::MoveFileW(temp.c_str(), fopin.c_str());
					}
				}
			}
		}
	};


	static bool TestFile(const std::wstring &fp, unsigned long waitms = 0, unsigned long segms = 5)
	{
		while (true)
		{
			HANDLE hfile = ::CreateFileW(fp.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if (hfile != INVALID_HANDLE_VALUE)
			{
				//the specified file exists and available to access
				CloseHandle(hfile);
				return true;
			}

			Sleep(segms);
			if (waitms > 0)
			{
				if (waitms <= segms)
					return false;
				waitms -= segms;
			}
		}
	};

};

#endif
